pepperstad.JobService = function (opts) {

    this.jobs = [];

    var resourceLoader = opts.resourceLoader;
    var occupations = resourceLoader.get("occupations");
    var self = this;

    /**
     * Requires vacant jobs to have been generated first
     * @param people
     */
    this.assignJobs = function (people) {
        _.each(people, function (person) {
            assignJob(person);
        });
        reportJobsByType();
    };

    function assignJob(person) {
        var job = null;
        var sortedSkills = _.sortBy(person.skills, function (skill) {
            return -skill.level;
        });
        // first look for a job best matching the person's skills
        _.find(sortedSkills, function (skill) {
            job = findJobForSkill(skill.name);
            return !!job;
        });
        // if no job was found, look for a vacant "general worker" job
        if (!job) {
            job = _.find(self.jobs, function (job) {
                return !job.employee && job.type == "Worker";
            });
        }
        if (job) {
            person.job = job;
            job.employee = person;
        }
    }

    function findJobForSkill(skillName) {
        // simply finds the first available job that desires the skill
        return _.find(self.jobs, function (job) {
            return !job.employee && _.contains(job.getSkillsFromTypeDefinition(), skillName);
        });
    }

    /**
     * Generates jobs based on buildings
     * @param oStory
     * @param aStructures
     */
    this.generateVacantJobs = function (oStory, aStructures) {
        // generates jobs from buildings
        _.each(aStructures, function (structure) {
            addJobs(structure);
        });
        // generates jobs that aren't located in a fixed place
        // based on story
        _.each(oStory.roamingJobs, function (job) {
            for (var i = 0; i < job.quantity; i++) {
                addJob(job.name);
            }
        });
        log("Vacant jobs generated: " + this.jobs.length);
    };

    function reportJobsByType() {
        var byType = {}, type;
        _.each(self.jobs, function (job) {
            type = byType[job.type];
            if (_.isUndefined(type)) {
                byType[job.type] = {
                    type: job.type,
                    count: 0,
                    assigned: 0,
                    unassigned: 0
                }
            }
            byType[job.type].count += 1;
            if (job.employee)
                byType[job.type].assigned += 1;
            else
                byType[job.type].unassigned += 1;
        });
        _.each(byType, function (jobType) {
            log(_.str.sprintf("\t%-15s %4s jobs %4s assigned", jobType.type, jobType.count, jobType.assigned));
        });
    }

    function addJobs(structure) {
        var parts, jobQuantity, jobType, job;
        _.each(structure.getAvailableJobsFromTypeDefinition(), function (jobDef) {
            parts = jobDef.split(":");
            jobQuantity = parts[0];
            jobType = parts[1];
            for (; jobQuantity > 0; jobQuantity--) {
                job = addJob(jobType, structure);
                structure.addJob(job);
            }
        });
    }

    function addJob(type, structure) {
        var job = new pepperstad.Job({
            type: type,
            structure: structure,
            resourceLoader: resourceLoader
        });
        self.jobs.push(job);
        return job;
    }
};

pepperstad.Job = function (opts) {
    this.type = opts.type;
    this.structure = opts.structure || null;
    this.employee = opts.employee || null;

    var resourceLoader = opts.resourceLoader,
        occupationTypeDefinition = null,
        self = this;

    this.getSkillsFromTypeDefinition = function () {
        return getOccupationDefinition().properties.skills;
    };

    function getOccupationDefinition() {
        if (occupationTypeDefinition) return occupationTypeDefinition;

        var aOccupationDefinitions = resourceLoader.get("occupations");
        occupationTypeDefinition = _.find(aOccupationDefinitions,
            function (occupationTypeDef) {
                return occupationTypeDef.name === self.type;
            });
        return occupationTypeDefinition;
    }
};
